﻿using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;

namespace SBPweb.Forge.Collections
{

    public class AutoSortingObservableList<TItem> : ObservableList<TItem>
        where TItem : INotifyPropertyChanged
    {

        #region Private fields

        private bool isAutoSortingActive = false;
        private bool isSorting = false;
        private Collection<TItem> collHandlers = new Collection<TItem>();

        #endregion

        #region Constructor logic

        public AutoSortingObservableList()
            : base()
        {
            AutoSortingTriggerPropertyNames = new Collection<string>();
        }

        public AutoSortingObservableList(int capacity)
            : base(capacity)
        {
            AutoSortingTriggerPropertyNames = new Collection<string>();
        }

        public AutoSortingObservableList(IEnumerable<TItem> list)
            : base(list)
        {
            AutoSortingTriggerPropertyNames = new Collection<string>();
            RegisterHandler(list);
        }

        #endregion

        #region Property accessors

        public bool IsAutoSortingActive
        {
            get
            {
                return isAutoSortingActive;
            }
            set
            {
                if (isAutoSortingActive == false)
                {
                    isAutoSortingActive = true;
                    AutoSort();
                }
                else
                {
                    isAutoSortingActive = false;
                }
            }
        }

        public Comparison<TItem> AutoSortingComparison
        {
            get;
            set;
        }

        public Collection<string> AutoSortingTriggerPropertyNames
        {
            get;
            protected set;
        }

        #endregion

        #region Methods

        protected override void AddItem(TItem item)
        {
            RegisterHandler(item);
            base.AddItem(item);
        }

        protected override void ClearItems()
        {
            foreach (TItem i in ItemsAsList)
            {
                UnregisterHandler(i);
            }
            base.ClearItems();
        }

        protected override void InsertItem(int index, TItem item)
        {
            RegisterHandler(item);
            base.InsertItem(index, item);
        }

        protected override void RemoveItem(TItem item)
        {
            UnregisterHandler(item);
            base.RemoveItem(item);
        }

        protected override void SetItem(int index, TItem item)
        {
            TItem oldItem = this[index];
            UnregisterHandler(oldItem);
            RegisterHandler(item);
            base.SetItem(index, item);
        }

        protected virtual void AutoSort()
        {
            lock (this)
            {
                if (!isAutoSortingActive)
                {
                    return;
                }

                if (isSorting)
                {
                    return;
                }

                isSorting = true;
                if (AutoSortingComparison != null)
                {
                    Sort(AutoSortingComparison);
                }
                else
                {
                    Sort();
                }
                isSorting = false;
            }
        }

        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            AutoSort();
        }

        private void RegisterHandler(TItem item)
        {
            lock (collHandlers)
            {
                if (collHandlers.Contains(item))
                {
                    return;
                }
                item.PropertyChanged += new PropertyChangedEventHandler(ItemPropertyChanged);
                collHandlers.Add(item);
            }
        }

        private void RegisterHandler(IEnumerable items)
        {
            foreach (TItem item in items)
            {
                RegisterHandler(item);
            }
        }

        private void UnregisterHandler(TItem item)
        {
            lock (collHandlers)
            {
                if (!collHandlers.Contains(item))
                {
                    return;
                }
                item.PropertyChanged -= new PropertyChangedEventHandler(ItemPropertyChanged);
                collHandlers.Remove(item);
            }
        }

        private void UnregisterHandler(IEnumerable items)
        {
            foreach (TItem item in items)
            {
                UnregisterHandler(item);
            }
        }

        private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (AutoSortingTriggerPropertyNames.Count == 0 || AutoSortingTriggerPropertyNames.Contains(e.PropertyName))
            {
                AutoSort();
            }
        }

#if SILVERLIGHT
        public static implicit operator AutoSortingObservableList<TItem>(ObservableCollection<TItem> collection)
        {
            return new AutoSortingObservableList<TItem>(collection);
        }
#endif

        #endregion

    }

}